ggplot2 is well-known for creating plots. Thanks to our sf and terra, we can exploit all amazing ggplot2 functions.
In general, on ggplot2:
well-suited for multi-dimensional data
expects data (frames) as input
components of the plot are added as layers
plot_call + layer_1 + layer_2 + ... + layer_n
From tmap to ggplot2
Reminder: We played around with tmap yesterday, and the results were already pretty nice. ggplot2 allows us to customize our maps even more, draw on previous knowledge of the package and increase the possibilities to combine maps, plots, and more.
The good thing: the inner logic of tmap and ggplot2 is the same and is based on the grammar of graphics.
If you are new to ggplot2, you might want to check out:
# load district shapefilegerman_districts <- sf::read_sf("./data/VG250_KRS.shp")# load district attributesattributes_districts <- readr::read_csv2("./data/attributes_districts.csv") |> dplyr::mutate(ecar_share =as.numeric(ecar_share))# join datagerman_districts_enhanced <- german_districts |> dplyr::left_join(attributes_districts, by ="AGS")# load states shapefilegerman_states <- sf::read_sf("./data/VG250_STA.shp")
Here’s a first basic map
# a simple first map ggplot() +geom_sf(data = german_districts_enhanced)
Making a plan
This map will be our canvas for the ongoing session. There are hundreds of options to change this map. We will cover at least some essential building blocks:
THE MAP: adding attributes, choose from colors/palettes, adding layers
THE LEGEND: position, sizes, display
THE ENVIRONMENT: choosing from themes and build your own
THE META-INFORMATION: titles and sources
THE EXTRAS: scales and compass
If your working on your maps, the ggplot2 cheatsheets will help you with an overview of scales, themes, labels, facets, and more.
The map layer: a basis
# easy fill with colorggplot() +geom_sf(data = german_districts_enhanced, fill ="purple", color ="blue" )
Add the aesthetics
We’ll concentrate on mapping the e-car share on the district level.
# map aetheticsggplot() +geom_sf(data = german_districts_enhanced, # add the attribute we want to mapaes(fill = ecar_share) ) +# choose a continuous palette scale_fill_continuous()
The map layer
Are you having trouble choosing the right color? There are some excellent tutorials out there, f.e. by Michael Toth.
# change color paletteggplot() +geom_sf(data = german_districts_enhanced, aes(fill = ecar_share) ) +# readable with color vision deficienciesscale_fill_viridis_c(option ="plasma")
# the shapefile includes polygons of oceans and lakes# easy fix on the fly when you know your datagerman_states <- german_states |> dplyr::filter(GF ==4)# add layer with German statesggplot() +geom_sf(data = german_districts_enhanced, aes(fill = ecar_share), color =NA ) +scale_fill_viridis_c(option ="plasma", direction =-1 ) +# add another layergeom_sf(data = german_states, # filling transparentfill ="transparent",# color of borderscolor ="black", # size of borderssize =1 )
Dealing with the Legend
You can deal with everything concerning the legend (labels, titles, width…) within the scale argument. The only thing you cannot change here is the position in relation to the map.
ggplot() +geom_sf(data = german_districts_enhanced, aes(fill = ecar_share), color =NA ) +scale_fill_viridis_c(option ="plasma",direction =-1,# add a legend titlename ="E-Car Share",# adjust legendguide =guide_legend(# turn it horizontaldirection="horizontal",# put the labels# under the legend barlabel.position ="bottom" ) ) +geom_sf(data = german_states, fill ="transparent", color ="black" ) # check the help file for more options ?guide_legend
Save and reuse
Maps produced with ggplot2 are standard objects like any other object in R (they are lists). We can just assign them to re-use, plot later, and add map layers.
Furthermore, you can save them just as any ggplot2 graph. The ggsave() function automatically detects the file format. You can also define the height, width, and dpi, which is particularly useful to produce high-class graphics for publications.
Save and reuse
# assign to objectecar_map <-ggplot() +geom_sf(data = german_districts_enhanced, aes(fill = ecar_share), color =NA ) +scale_fill_viridis_c(option ="plasma",direction =-1,name ="E-Car Share",guide =guide_legend(direction="horizontal",label.position ="bottom" ) ) +geom_sf(data = german_states, fill ="transparent", color ="black" ) # save as png-file# ggsave("ecar_map.png", ecar_map, dpi = 300)
Get rid of everything?!
The theme controls all ‘non-data’ display. If you want to get rid of the default ggplot2 theme, you can do so. But instead of getting rid of everything, you might want to try out the built-in themes.
# use the object ecar_map as base layerecar_map +# empty your themetheme_void() # ... or add another# theme_bw()# theme_gray()# theme_light()# check all themes here# ?theme
Build your own theme
# building a themeecar_map +theme_void() +# bold text elementstheme(title =element_text(face ="bold"), # move legend to bottom of maplegend.position ="bottom", # change background colorpanel.background =element_rect(fill ="lightgrey") )
Adding labs
There is one necessary step to do. You should always make sure to include and cite your data sources. Especially in graphs and maps, you can use a short version to include them in the description directly.
Our code in total has already grown pretty much. Without going into too much detail, the next slides showcase some more changes you can do with your maps
A map is never finished until you decide not to work on it anymore.
Creating a city layer for city labels
# create a german city layer by choosing the five districts# with the highest pop densitydistricts_centroids <- german_districts_enhanced |># calculate pop_dens dplyr::mutate(pop_dens = population / sf::st_area(german_districts_enhanced) ) |># filter top 5 observation with highs pop_dens dplyr::top_n(5, pop_dens) |># take the centroid of each polygon and turn to# polygon file into a vector sf::st_centroid() city_coordinates <- districts_centroids |> sf::st_coordinates() |>as.data.frame() german_cities <- dplyr::bind_cols(districts_centroids, city_coordinates) |># add some city names as labels dplyr::bind_cols(data.frame(names =c("City 1", "City 2", "City 3", "City 4", "City 5") ) )german_cities |> dplyr::select(pop_dens, X, Y)
Simple feature collection with 5 features and 3 fields
Geometry type: POINT
Dimension: XY
Bounding box: xmin: 432953.5 ymin: 5283785 xmax: 670634.3 ymax: 5998091
Projected CRS: ETRS89 / UTM zone 32N
# A tibble: 5 × 4
pop_dens X Y geometry
[1/m^2] <dbl> <dbl> <POINT [m]>
1 2.03 501787. 5964329. (501786.6 5964329)
2 0.786 432954. 5947975. (432953.5 5947975)
3 1.25 469708. 5935810. (469708.3 5935810)
4 0.933 504505. 5283785. (504505.2 5283785)
5 2.07 670634. 5998091. (670634.3 5998091)
Add City Labels
Using geom_label to get a text box that holds a character string associated with an X- and Y-coordinate
ecar_map +# add the labelgeom_label(data = german_cities, # don't need sf object but columns # with x- and y-coordinateaes(x = X, y = Y, # column holding the character# vector with stringslabel = names ),# size of labelssize =3,# transparencyalpha = .8 )
ggplot2 and raster data
You can also use ggplot2 to create maps with raster data. There are several ways to do so. The easiest way is using the tidyterra package
In some specific circumstances, we might realize that ggplot2 is super powerful but just not designed to build maps. Typical features of maps are not in the package, like a compass or scale bars.
This is where other packages might need to be installed. The good thing: Elements of the package ggspatial can be included as ggplot2 layer. Checkout Github.
The extras
ggspatial allows you to add, f.e. a scale bar and a north arrow.
# add scalebar and north arrowecar_map + ggspatial::annotation_scale(location ="br" ) + ggspatial::annotation_north_arrow(location ="tr", style = ggspatial::north_arrow_minimal() )